package com.google.jessewilson.codesearch.intellij;

import com.google.jessewilson.codesearch.api.Line;
import com.google.jessewilson.codesearch.io.Path;
import com.google.jessewilson.codesearch.io.Paths;
import com.google.jessewilson.codesearch.util.EventListEdtProcessor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.PsiShortNamesCache;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Find the file closest to another file somewhere within the project.
 *
 * @author jessewilson
 */
class ProjectFilesFinder implements EventListEdtProcessor.Processor<Line> {

  private final PsiShortNamesCache shortNamesCache;

  public ProjectFilesFinder(Project project) {
    this.shortNamesCache = PsiManager.getInstance(project).getShortNamesCache();
  }

  public boolean isProcessed(Line line) {
    return line.getCodeFile() == null
        || line.getCodeFile().getCodeFileExtra(ProjectFiles.class) != null;
  }

  public Line process(Line line) {
    ProjectFiles projectFiles = findAllMaximumMatches(line.getCodeFile().getPath());
    line.getCodeFile().setCodeFileExtra(ProjectFiles.class, projectFiles);
    line.getCodeFile().setAvailableLocally(!projectFiles.getFiles().isEmpty());
    return line;
  }

  /**
   * Returns the local files with paths most similar to the specified path.
   */
  public ProjectFiles findAllMaximumMatches(Path path) {
    String fileName = path.getFileName();
    if(fileName == null) {
      return new ProjectFiles(Collections.<VirtualFile>emptyList());
    }

    List<VirtualFile> bestMatches = new ArrayList<VirtualFile>(1);
    int bestMatchLength = 0;
    for(PsiFile file : shortNamesCache.getFilesByName(fileName)) {
      VirtualFile virtualFile = file.getVirtualFile();
      if(virtualFile == null) {
        continue;
      }

      Path psiFilePath = Path.fromString(virtualFile.getPath());
      int commonPostfix = Paths.commonPathLength(path, psiFilePath);
      if(commonPostfix > bestMatchLength) {
        bestMatchLength = commonPostfix;
        bestMatches.clear();
      }
      if(commonPostfix == bestMatchLength) {
        bestMatches.add(virtualFile);
      }
    }

    return new ProjectFiles(bestMatches);
  }
}
